#include "MemoryMappedFile.h"
#include <fstream>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>

/// <summary>	Default constructor. </summary>
CMemoryMappedFile::CMemoryMappedFile(void) : m_ulFileSize (0), m_ulMapDelta (0), m_ulMapSize (0), m_pData (0), m_pMap(0), m_pFile(0) {
  //OutputDebugString("CMemoryMappedFile default constructor");
}

/// <summary> Parametric Constructor </summary>
/// <param name="sFileName">  File name. </param>
CMemoryMappedFile::CMemoryMappedFile(const char* sFileName, unsigned long mapSize) : m_ulFileSize (0), m_ulMapDelta (0), m_ulMapSize (0), m_pData (0), m_pMap(0), m_pFile(0) {
  //OutputDebugString("CMemoryMappedFile parametric constructor");
  OpenFile(sFileName, mapSize);
}

/// <summary>	Destructor. </summary>
CMemoryMappedFile::~CMemoryMappedFile(){
  //OutputDebugString("CMemoryMappedFile destructor");
	CloseMap();
  if (m_pFile) delete m_pFile;
}

/// <summary> Open the file. </summary>
/// <param name="sFileName">  File name. </param>
/// <returns> true if it succeeds, false if it fails. </returns>
bool CMemoryMappedFile::OpenFile(const char* sFileName, unsigned long mapSize){
	if (GetFileSize(sFileName)) {
	  try {
      if (mapSize > m_ulFileSize ){
        std::ofstream file(sFileName, std::ios::app | std::ios::out | std::ios::binary );
	      char* buf = new char[mapSize - m_ulFileSize];
	      memset(buf,0,mapSize - m_ulFileSize);
        file.write(buf,mapSize - m_ulFileSize);
        file.close();
		    delete[] buf;
		    m_ulFileSize = mapSize;
      }
      if (m_pFile) delete m_pFile;
	    m_pFile = new boost::interprocess::file_mapping(sFileName,boost::interprocess::read_write);
      //OutputDebugString("Sucessful in CMemoryMappedFile::OpenFile");
      return true;
	  }
	  catch(...) {
      m_ulFileSize = 0;
		  //OutputDebugString("Exception thrown in CMemoryMappedFile::OpenFile");
      return false;
    } 
	}
  //OutputDebugString("Filesize is zero in CMemoryMappedFile::OpenFile");
  return false;
}

/// <summary>	Gets the file size. </summary>
/// <param name="sFileName">	Filename. </param>
/// <returns>	The file size. </returns>
unsigned long CMemoryMappedFile::GetFileSize(const char* sFileName) {
	try {
    std::ifstream myfile(sFileName, std::ios::binary | std::ios::in);
		std::streampos old_pos = myfile.tellg();
		myfile.seekg (0, std::ios::end);
		m_ulFileSize = (unsigned long)myfile.tellg();
		myfile.seekg(old_pos);
		myfile.close();
    if (myfile.bad()) {
      //OutputDebugString("Error in CMemoryMappedFile::GetFileSize");
			m_ulFileSize = 0;
    } 
    //else
    //OutputDebugString("Sucessful CMemoryMappedFile::GetFileSize");
	}
	catch (...) {
		m_ulFileSize = 0;
	  //OutputDebugString("Exception thrown in CMemoryMappedFile::GetFileSize");
	}
	return m_ulFileSize;
}

/// <summary>	Map file region. </summary>
/// <param name="ulStart"> The region starting point. </param>
/// <param name="ulSize"> Size of the region. </param>
/// <returns>	pointer to the beginning of data. </returns>
void* CMemoryMappedFile::MapFileRegion(unsigned long ulStart, unsigned long ulSize) {
	if (m_pFile) {
    m_ulMapDelta = ulStart % boost::interprocess::mapped_region::get_page_size();
	  m_ulMapSize = ( ( ulSize && (ulStart + ulSize) <= m_ulFileSize) ? ulSize : ( m_ulFileSize - ulStart ) ) + m_ulMapDelta;
		try {
	    CloseMap();
      if ( !m_pMap ) m_pMap = new boost::interprocess::mapped_region(*m_pFile, boost::interprocess::read_write, ulStart - m_ulMapDelta, m_ulMapSize);
		}
		catch(...) {
  	  //OutputDebugString("Exception thrown in CMemoryMappedFile::MapFileRegion");
			CloseMap();
      return 0;
		}
	  m_pData = m_pMap->get_address();
	  //OutputDebugString("Successful in CMemoryMappedFile::MapFileRegion");
	  return GetPointer();
	}
  //OutputDebugString("File not open in CMemoryMappedFile::MapFileRegion");
  return 0;
}

/// <summary>	Closes the file map. </summary>
/// <returns>	pointer to null. </returns>
void CMemoryMappedFile::CloseMap() {
  if ( m_pMap ){
    if( m_pData )
		  m_pMap->flush();
    delete m_pMap;
    m_pMap = 0;
  }
  m_pData = 0;
  //OutputDebugString("CMemoryMappedFile::CloseMap");
  return;
}

/// <summary>	Gets a pointer to the data at an specified ulOffset. </summary>
/// <param name="ulOffset">	The data offset. </param>
/// <returns>	null if it fails, else the pointer. </returns>
void* CMemoryMappedFile::GetPointer(unsigned long ulOffset) {
  void* ptr;
	if (m_pData && (ulOffset + m_ulMapDelta < m_ulMapSize))
	  ptr = (void*)((unsigned long long)m_pData + (unsigned long long)(ulOffset + m_ulMapDelta));
  else
    ptr = 0;
  //OutputDebugString("CMemoryMappedFile::GetPointer");
  return ptr;
}
